using System;
using Community.CsharpSqlite.Entity;

namespace Community.CsharpSqlite
{
    public  class Random
    {
        /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains code to implement a pseudo-random number
    ** generator (PRNG) for SQLite.
    **
    ** Random numbers are used by some of the database backends in order
    ** to generate random integer keys for tables or random filenames.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2009-12-07 16:39:13 1ed88e9d01e9eda5cbc622e7614277f29bcc551c
    **
    **  $Header: Community.CsharpSqlite/src/random_c.cs,v bcbd36f24b23 2010/02/18 17:35:24 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"



        public static sqlite3PrngType sqlite3Prng = new sqlite3PrngType();
        /*
    ** Get a single 8-bit random value from the RC4 PRNG.  The Mutex
    ** must be held while executing this routine.
    **
    ** Why not just use a library random generator like lrand48() for this?
    ** Because the OPCode.OP_NewRowid opcode in the VDBE depends on having a very
    ** good source of random numbers.  The lrand48() library function may
    ** well be good enough.  But maybe not.  Or maybe lrand48() has some
    ** subtle problems on some systems that could cause problems.  It is hard
    ** to know.  To minimize the risk of problems due to bad lrand48()
    ** implementations, SQLite uses this random number generator based
    ** on RC4, which we know works very well.
    **
    ** (Later):  Actually, OPCode.OP_NewRowid does not depend on a good source of
    ** randomness any more.  But we will leave this code in all the same.
    */

        private static byte randombyte()
        {
            byte t;

            /* The "wsdPrng" macro will resolve to the pseudo-random number generator
      ** state vector.  If writable static data is unsupported on the target,
      ** we have to locate the state vector at run-time.  In the more common
      ** case where writable static data is supported, wsdPrng can refer directly
      ** to the "sqlite3Prng" state vector declared above.
      */
#if SQLITE_OMIT_WSD
struct sqlite3PrngType *p = &GLOBAL(struct sqlite3PrngType, sqlite3Prng);
//# define wsdPrng p[0]
#else
            //# define wsdPrng sqlite3Prng
            sqlite3PrngType wsdPrng = sqlite3Prng;
#endif


            /* Initialize the state of the random number generator once,
** the first time this routine is called.  The seed value does
** not need to contain a lot of randomness since we are not
** trying to do secure encryption or anything like that...
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
            if (!wsdPrng.isInit)
            {
                int i;
                var k = new byte[256];
                wsdPrng.j = 0;
                wsdPrng.i = 0;
                sqlite3OsRandomness(sqlite3_vfs_find(""), 256, ref k);
                for (i = 0; i < 255; i++)
                {
                    wsdPrng.s[i] = (byte) i;
                }
                for (i = 0; i < 255; i++)
                {
                    wsdPrng.j = (byte) (wsdPrng.j + wsdPrng.s[i] + k[i]);
                    t = wsdPrng.s[wsdPrng.j];
                    wsdPrng.s[wsdPrng.j] = wsdPrng.s[i];
                    wsdPrng.s[i] = t;
                }
                wsdPrng.isInit = true;
            }

            /* Generate and return single random byte
      */
            wsdPrng.i++;
            t = wsdPrng.s[(byte) wsdPrng.i];
            wsdPrng.j = (byte) (wsdPrng.j + t);
            wsdPrng.s[(byte) wsdPrng.i] = wsdPrng.s[wsdPrng.j];
            wsdPrng.s[wsdPrng.j] = t;
            t += wsdPrng.s[(byte) wsdPrng.i];
            return wsdPrng.s[t];
        }

        /*
    ** Return N random bytes.
    */

        public static void Randomness(int N, ref long pBuf)
        {
            //byte[] zBuf = new byte[N];
            pBuf = 0;
#if SQLITE_THREADSAFE
sqlite3_mutex mutex = MutexHelper.MutexAlloc( MutexType.SQLITE_MUTEX_STATIC_PRNG );
#endif
            MutexHelper.MutexEnter(Global.mutexData);
            while (N-- > 0)
            {
                pBuf = (uint) ((pBuf << 8) + randombyte()); //  zBuf[N] = randombyte();
            }
            MutexHelper.MutexLeave(Global.mutexData);
        }

#if !SQLITE_OMIT_BUILTIN_TEST
        /*
** For testing purposes, we sometimes want to preserve the state of
** PRNG and restore the PRNG to its saved state at a later time, or
** to reset the PRNG to its initial state.  These routines accomplish
** those tasks.
**
** The sqlite3_test_control() interface calls these routines to
** control the PRNG.
*/
        private static sqlite3PrngType sqlite3SavedPrng;

        public static void PrngSaveState()
        {
            sqlite3SavedPrng = sqlite3Prng.Copy();
            //      memcpy(
            //  &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
            //  &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
            //  sizeof(sqlite3Prng)
            //);
        }

        public static void PrngRestoreState()
        {
            sqlite3Prng = sqlite3SavedPrng.Copy();
            //memcpy(
            //  &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
            //  &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
            //  sizeof(sqlite3Prng)
            //);
        }

        public static void PrngResetState()
        {
            sqlite3Prng.isInit = false; //  GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
        }
#endif
        //* SQLITE_OMIT_BUILTIN_TEST */
    }
}